<?php

namespace app\queue\stomp;

use app\queue\BaseConsumer;
use common\model\DelayOrderModel;
use common\model\EntityModel;
use common\model\OriginalPlanNodeModel;
use common\model\ReviewFeedbackModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\UserModel;
use common\service\OptionsService;
use common\service\StatusService;
use common\service\TaskUpstreamService;
use Webman\Stomp\Client;

/**
 * 综合消息通知队列
 */
class CommonMessagePushingConsumer extends BaseConsumer // implements RetryAbleConsumer
{

    // 要消费的队列名
    public $queue = 'common_message_pushing';

    // 连接名，对应 config/redis_queue.php 里的连接`
    public $connection = 'default';

    /**
     * 综合消息通知
     * @param $data
     * @throws \Throwable
     */
    public function doConsume($data)
    {
        try {
            // 解包消息
            list($source, $extra, $category, $messageReceiver, $messageReceiverCc, $messageKey, $messageGenerated) = $this->unPackMessage($data);
            $messageList = $messageGenerated;
            if (empty($messageList)) {
                // 处理默认接收人、抄送人
                $this->solveReceiver($category, $source, $messageReceiver, $messageReceiverCc);
                // 处理消息类型、通道
                $this->solveMessageTarget($category, $messageReceiver);
                // 按接收人匹配消息模板
                $messageList = $this->solveMessageFormat($source, $extra, $category, $messageReceiver);
            }
            $this->packUpMessage($messageList, $category);
        } catch (\Throwable $e) {
            trace(format_exception_info($e), "ERR");
        }
    }

    /**
     * 解包消息
     * @param $pack
     * @return array
     */
    private function unPackMessage($pack)
    {
        $data = [
            'source' => [
                'id' => $pack['source_id'] ?? 0,
                'module_code' => $pack['source_module_code'] ?? '',
                'data' => [],
                'user_id_list' => split_array($pack['source_user_id'] ?? ''),
                'user_id_list_related' => [],
                'time' => $pack['time'] ?? '',
                'end_time' => $pack['end_time'] ?? '',
            ],
            'extra' => [
                'review_pass' => $pack['review_pass'] ?? 'default',
                'node_all_delivered' => $pack['node_all_delivered'] ?? 'default',
            ],
            'category' => $pack['category'] ?? '',
            'receiver' => $pack['receiver'] ?? [],
            'receiver_cc' => $pack['receiver_cc'] ?? [],
            'key' => $pack['key'] ?? 0,
            'message_generated' => $pack['message_generated'] ?? [],
        ];
        if (!empty($data['source']['id'])) {
            switch ($data['source']['module_code']) {
                case 'demand_order':
                case 'review_feedback':
                    $data['source']['data'] = model(ReviewFeedbackModel::class)->find($data['source']['id']);
                    $data['source']['data']['show_name'] = $data['source']['data']['title'];
                    array_push($data['source']['user_id_list_related'], $data['source']['data']['created_by'] ?? 0, $data['source']['data']['proposer'] ?? 0);
                    break;
                case 'task':
                    $data['source']['data'] = model(TaskModel::class)->find($data['source']['id']);
                    $data['source']['data']['show_name'] = $this->dealTaskShowName($data['source']['data']);
                    break;
                case 'delay_order':
                    $data['source']['data'] = model(DelayOrderModel::class)->find($data['source']['id']);
                    $data['source']['data']['show_name'] = '';
                    break;
                case 'original_plan_node':
                    $data['source']['data'] = model(OriginalPlanNodeModel::class)->find($data['source']['id']);
                    $data['source']['data']['show_name'] = $data['source']['data']['name'];
                    $data['source']['time'] = $data['source']['data']['delivery_time'];
                    break;
            }
        }
        return array_values($data);
    }

    /**
     * 处理任务名展示
     * @param $task
     * @return string|void
     */
    private function dealTaskShowName($task = [])
    {
        $res = '';
        if (empty($task)) {
            return $res;
        }
        $entity = model(EntityModel::class)
            ->where(['id' => $task['entity_id']])
            ->field('initial_episode_code,showings_number,name')->select();
        if (!empty($entity)) {
            $res = implode('/', array_shift($entity));
        }
        return $res;
    }

    /**
     * 处理消息接收人
     * @param $message
     * @param $category
     */
    private function solveReceiver($category, $source, &$receiver, $receiverCc = [])
    {
        if (empty($receiver)) {
            switch ($category) {
                case "demand_order_confirm":
                case "demand_order_publish":
                    break;
                case "task_assign":
                case 'task_update_plan_end_time':
                case 'task_update_plan_delivery_time':
                case "task_repair":
                case "task_deprecate":
                case "task_reopen":
                case "task_upstream_all_delivery":
                case 'task_review_finish':
                    $receiver = $this->dealTaskMemberReceiver($source['data']);
                    break;
                case "task_delivery":
                    $receiver = $this->dealTaskDownStreamMemberReceiver($source['data']);
                    break;
                case "task_review":
                    break;
                case "delay_order_review":
                    break;
                case "delay_order_review_finish":
                case "node_delivery_on_time":
                    break;
                default:
                    break;
            }
        }
        // 此处清理重复人员，后续若同一人有不同模板调用，此处应修改
        // todo: 是否判断自己的操作是否需要发消息给自己
        $receiver = array_column($receiver, null, 'user_id');
        $receiver = array_map(fn($people) => array_merge($people, ['receiver_type' => 'normal']), $receiver);
        $receiverUserIdList = array_column($receiver, 'user_id');
        // 清理抄送重复人员 优先级 正常通知 > 抄送
        $receiver = array_merge($receiver,
            array_map(
                fn($people) => array_merge($people, ['receiver_type' => 'cc']),
                array_filter($receiverCc, fn($people) => (!in_array($people['user_id'], $receiverUserIdList))))
        );
    }

    /**
     * 处理任务类型消息 下游默认接收人：执行人、参与人
     * @param $task
     * @return array|array[]
     */
    private function dealTaskDownStreamMemberReceiver($task = [])
    {
        $res = [];
        if (empty($task)) {
            return $res;
        }
        // 查出所有下游工序任务
        $taskDownStreamList = TaskUpstreamService::getInstance()->getDownstreamTaskList($task['id']);
//        $taskDownStreamList = TaskWorkflowBaseService::getInstance()->getTaskDownstream($task['id']);
        // 对每个任务检查上游交付情况
        $taskReadyList = [];
        foreach ($taskDownStreamList as $downStreamTask) {
            if ($downStreamTask['task_status'] == StatusService::getInstance()->byCode('closed')['id']) {
                continue;
            }
            $upstreamDoneStatus = TaskUpstreamService::getInstance()->getUpstreamTaskDoneStatus($downStreamTask); // todo
            if ($upstreamDoneStatus['flag']) {
                $taskReadyList[] = $downStreamTask;
            }
        }
        foreach ($taskReadyList as $downStreamTask) {
            $receiverTemp = $this->dealTaskMemberReceiver($downStreamTask);
            $res = array_merge($res, $receiverTemp ?? []);
        }
        return $res;
    }

    /**
     * 处理任务类型消息默认接收人：执行人、参与人
     * @param $task
     * @return array|array[]
     */
    private function dealTaskMemberReceiver($task = [])
    {
        $res = [];
        if (empty($task)) {
            return $res;
        }
        $userList = model(TaskMemberModel::class)->where(['task_id' => $task['id']])->field(TaskMemberModel::EXECUTOR_FIELD . ',' . TaskMemberModel::ASSIGNEE_FIELD)->select();
        $executorList = array_map(fn($people) => ['user_id' => $people, 'role' => 'executor'], array_column($userList, TaskMemberModel::EXECUTOR_FIELD));
        $assigneeList = array_map(fn($people) => ['user_id' => $people, 'role' => 'assignee'], array_column($userList, TaskMemberModel::ASSIGNEE_FIELD));
        $res = array_merge($executorList, $assigneeList);
        return $res;
    }

    /**
     * 设置消息类型
     * @param $category
     * @param $type
     * @param $key
     */
    private function solveMessageTarget($category, &$receiver)
    {
        $typeList = ['dot', 'popover', 'modal'];
        foreach ($receiver as &$recv) {
            $type = '';
            switch ($category) {
                case "node_delivery_on_time":
                    if ($recv['receiver_type']) {
                        $type = 'dot';
                    }
                    break;
                case "demand_order_confirm":
                    if ($recv['receiver_type']) {
                        $type = 'dot';
                    }
                    break;
                case "demand_order_publish":
                    if ($recv['receiver_type']) {
                        $type = 'popover';
                    }
                    break;
                case 'entity_relation_change':
                    if ($recv['receiver_type']) {
                        $type = 'popover';
                    }
                    break;
                case 'task_assign':
                case 'task_update_plan_end_time':
                case 'task_update_plan_delivery_time':
                case 'task_repair':
                case 'task_upstream_all_delivery':
                case "task_delivery":
                case "delay_order_review_finish":
                    if ($recv['receiver_type']) {
                        $type = 'dot';
                    }
                    break;
                case "delay_order_review":
                case 'task_review':
                case 'task_review_finish':
                case 'task_deprecate':
                case 'task_reopen':
                    if ($recv['receiver_type']) {
                        $type = 'popover';
                    }
                    break;
                case 'original_plan_node_delivery':
                    $type = 'dot';
                    break;
                default:
                    $type = 'dot';
                    break;
            }
            if (in_array($type, $typeList)) {
                $recv['message_type'] = $type;
            }
        }
    }

    /**
     * 匹配消息模板
     * @param $source
     * @param $category
     * @param $receiver
     * @return array
     */
    private function solveMessageFormat($source, $extra, $category, $receiver)
    {
        $res = [];
        // 对每个接收人匹配相应的消息模板
        $userIdList = array_column($receiver, 'user_id');
        $userIdList = array_merge($userIdList, $source['user_id_list'], $source['user_id_list_related']);
        $userList = model(UserModel::class)->where(['id' => ['in', implode(',', $userIdList)]])->field('id,name,union_id')->select();
        $userIdList = array_column($userList, 'id');
        $userIdMap = array_column($userList, null, 'id');
        // 拼接多个操作人姓名
        $operatorString = implode(',', array_column(array_filter($userList, fn($people) => in_array($people['id'], $source['user_id_list'])), 'name'));
        foreach ($receiver as $people) {
            $temp = [];
            // 用户不存在则跳过
            if (!in_array($people['user_id'], $userIdList)) {
                continue;
            }
            switch ($category) {
                case 'original_plan_node_delivery':
                    $temp = [
                        'param' => [
                            'first' => '上游原计划节点交付通知',
                            'note_text' => '上游计划交付节点【' . $source['data']['show_name'] . '】'
                                . ($extra['node_all_delivered'] == 'yes' ? '已' : '未') . '全部交付',
//                            'remark' => $source['data']['remark'],
                            'operate_at' => $source['time'],
                            'link_id' => $source['id'],
                            'link_module_code' => $source['module_code'],
                        ],
                        'notice_type' => 'message_task',
                    ];
                    break;
                case 'demand_order_confirm':
                    $temp = [
                        'param' => [
                            'first' => '需求确认通知',
                            'note_text' => $source['data']['show_name'],
                            'remark' => $source['data']['remark'],
                            'operate_at' => $source['time'],
                            'end_time' => $source['end_time'],
                            'created_by' => $userIdMap[$source['data']['created_by']]['name'] ?? '',
                            'proposer' => $userIdMap[$source['data']['proposer']]['name'] ?? '',
                            'link_id' => $source['id'],
                            'link_module_code' => $source['module_code'],
                        ],
                        'notice_type' => 'message_demand',
                    ];
                    break;
                case 'demand_order_publish':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '需求下发通知',
                                'note_text' => $source['data']['show_name'],
                                'remark' => $source['data']['remark'],
                                'operate_at' => $source['time'],
                                'created_by' => $userIdMap[$source['data']['created_by']]['name'] ?? '',
                                'proposer' => $userIdMap[$source['data']['proposer']]['name'] ?? '',
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_demand',
                        ];
                    }
                    break;
                case 'task_assign':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务分配通知',
                                'note_text' => $operatorString . '将【' . $source['data']['show_name'] . '】分配给你',
                                'operate_at' => $source['time'],
                                'plan_start_at' => $source['data']['plan_start_time'] ?? 0,
                                'plan_end_at' => $source['data']['plan_end_time'] ?? 0,
                                'plan_delivery_at' => $source['data']['plan_delivery_time'] ?? 0,
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case "node_delivery_on_time":
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '交付节点按时交付通知',
                                'note_text' => '上游交付节点【' . $source['data']['show_name'] . '】已全量按时交付',
                                'operate_at' => $source['delivery_time'],
                                'delivery_at' => $source['data']['delivery_time'] ?? 0,
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_update_plan_time':
                case 'task_update_plan_end_time':
                case 'task_update_plan_delivery_time':
                    $category = 'task_update_plan_time';
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务计划时间变更通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】计划时间发生变更',
                                'operate_at' => $source['time'],
                                'plan_start_at' => $source['data']['plan_start_time'] ?? 0,
                                'plan_end_at' => $source['data']['plan_end_time'] ?? 0,
                                'plan_delivery_at' => $source['data']['plan_delivery_time'] ?? 0,
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_review':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务审核通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】完成，待审核',
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_review_finish':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务审核结果通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】审核',
                                'review_pass' => $extra['review_pass'],
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_repair':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务返修通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】发起了返修',
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_delivery':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '上游交付通知',
                                'note_text' => '上游任务【' . $source['data']['show_name'] . '】已交付',
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_upstream_all_delivery':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '上游全部交付通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】的所有上游已交付',
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case 'task_deprecate':
                case 'task_reopen':
                    $roleText = $this->solveRoleTextTask($people['role']);
                    if (!empty($roleText)) {
                        $temp = [
                            'param' => [
                                'first' => '任务' . ($category == 'task_deprecate' ? '废除' : '启用') . '通知',
                                'note_text' => '任务【' . $source['data']['show_name'] . '】被' . ($category == 'task_deprecate' ? '废除' : '启用'),
                                'operate_at' => $source['time'],
                                'link_id' => $source['id'],
                                'link_module_code' => $source['module_code'],
                            ],
                            'notice_type' => 'message_task',
                        ];
                    }
                    break;
                case "delay_order_review":
                    $temp = [
                        'param' => [
                            'first' => '延期单待审批通知',
                            'note_text' => '延期单【' . $source['data']['show_name'] . '】待审批',
                            'operate_at' => $source['time'],
                            'link_id' => $source['id'],
                            'link_module_code' => $source['module_code'],
                        ],
                        'notice_type' => 'message_producer',
                    ];
                    break;
                case "delay_order_review_finish":
                    $temp = [
                        'param' => [
                            'first' => '延期单审批结果',
                            'note_text' => '延期单【' . $source['data']['show_name'] . '】审批',
                            'operate_at' => $source['time'],
                            'link_id' => $source['id'],
                            'link_module_code' => $source['module_code'],
                        ],
                        'notice_type' => 'message_task',
                    ];
                    break;
                default:
                    break;
            }
            if (!empty($temp)) {
                $res[] = array_merge($temp, [
                    'receiver' => $userIdMap[$people['user_id']]['union_id'],
                    'template_id' => OptionsService::getInstance()->getTaskRemindCommonTemplateId(),
                    'content_type' => 'common_' . $people['message_type'],
                    'is_popup' => ($people['message_type'] != 'dot') ? 1 : 0,
                    'message_key' => $category,
                ]);
            }
        }
        return $res;
    }

    /**
     * 打包消息
     * @param $messageList
     * @param $category
     */
    private function packUpMessage($messageList, $category)
    {
        foreach ($messageList as $message) {
            Client::send('send_notify_message_v2', build_queue_data_with_xu($message));
        }
    }

    /**
     * 处理任务角色文本
     * @param $roleCode
     * @return string
     */
    private function solveRoleTextTask($roleCode)
    {
        $roleCodeMap = [
            'executor' => '执行人',
            'assignee' => '参与人',
            'plan_reviewer' => '审核人',
            'plan_reviewer_party_b' => '外部审核人',
            'confirm_user' => '确认人',
            'reconfirm_user' => '确认人',
            'created_by' => '创建人',
            'proposer' => '提出人',
        ];
        return $roleCodeMap[$roleCode] ?? '';
    }

}
